import sys
import time
import usb
import ubinascii
import urandom
import uasyncio as asyncio

serial = usb.Serial("/dev/ttyUSB1", 500)
flag = 0

class Warning:
    def __init__(self, serial, SMSC = "+8613800393500", flag= 1):
        self.serial = serial                    # USB转虚拟串口serial
        self.warn_str = None                    # 警告消息字符串
        # self.priority = priority              # 优先级
        self.phone_number = None
        self.ack = 'OK'
        self.CMGS = "+CMGS:"
        self.SMSC = SMSC             # 作者使用的是濮阳的SMSC码，不可随意切换哦
        self.flag = flag             # 4G模块初始化一次就可以了
        self.storage_index = None     # 存储的短信索引
        # self.serial_isopened = False


    def Send_Message(self, warn_str, phone_number):

        start_time = time.time()

        self.phone_number = phone_number
        self.warn_str = warn_str

        print(f"warn_str = {self.warn_str}")

        if not isinstance(phone_number, str):
            print(f"请规范手机号传参:{phone_number}")
        
        if not self.flag:
            self.DistributionNetwork()

        # message = self._to_PDU()
        # if message is None:
        #     print("message, 这竟然是一个空值！")

        pdu_list = self._to_PDU()

        if pdu_list is None:
            print("PDU转换失败")
            return
        
        if self.serial.open(): 
            if self.__check_network_state():
                # print("进入这里了self.__check_network_state()")
                # self.__enable_continuous_communication(mode=2)

                self.serial.timeout_ms = 5000
                self.__send_stored_messages()
                for pdu, tp_length in pdu_list:
                    # print("进入这里了for pdu, tp_length in pdu_list")
                    if self.__SendPdu(pdu, tp_length):
                        print("网络下，发送成功")
                # self.__disable_continuous_communication()
            else:
                # 网络异常，存储到ME
                print("网络不可用, 存储到ME")
                for pdu, tp_length in pdu_list:
                    self.__store_to_me(pdu, tp_length)
        if self.serial.open():
            self.serial.timeout_ms = 500
            time.sleep(0.2)
            self.__check_storage_status()
            self.serial.close()
        end_time = time.time()
        print(f"执行时间：{end_time - start_time}秒")
        return ;
        

    def __enable_continuous_communication(self, mode=1):
        """启用 AT+CMMS 链路保持功能"""
        # mode=1: 自动超时关闭，mode=2: 手动控制关闭
        command = f"AT+CMMS={mode}"
        if self.command(command):
            print(f"AT+CMMS 已启用，模式: {mode}")
            time.sleep(1)  # 等待指令生效
            return True
        return False
    
    def __disable_continuous_communication(self):
        """关闭 AT+CMMS 链路保持"""
        # 通过发送空命令触发链路关闭（或设置 AT+CMMS=0）
        self.command("AT+CMMS=0")
        print("AT+CMMS 已关闭")


    def __SendPdu(self, pdu, tp_length):
        if self.serial.open():
            command = f'AT+CMGS={tp_length}\r\n'
            self.serial.write(command.encode())
            time.sleep(0.3)
            self.serial.write(pdu.encode())
            self.serial.write(bytes([26]))
            time.sleep(0.5)
            response = self.serial.read(200) 
            if self.ack in response or self.CMGS in response:
                # (f"发送成功")
                return True
            else:
                # print(f"发送失败")
                return False
            
        return ;

    def command(self, command="AT"):
        # 这是一个命令函数，读取AT指令, 如果读取成功就返回True,否则False.
        if self.serial.open():
            if not command.endswith('\r\n'):
                command += '\r\n'
            self.serial.write(command.encode())
            response = self.__ResponseMessage()
            if self.ack in response and response is not None:
                print(f"{command}命令执行成功\n得到信息:{response}")
                # self.serial.close()
                return True
            else:
                print(f"{command}命令执行失败\n得到信息:{response}")
                return False
        else:
            self.command(command)

    
    def __ResponseMessage(self):
        if self.serial.open():
            # 接受100个字节
            time.sleep(0.5)
            response = self.serial.read(100)
            return response
        else:
            self.__ResponseMessage()

    def init_SMSC(self):
        return self.flag

                    
    def DistributionNetwork(self):
        if self.command("AT"):
            # 直接设置为ECM模式, 仅发送短信
            if self.__Config_ECM_OnlyMessageSent():
                if self.__soft_restart():
                    if self.__Config_PDU_Mode():
                        if self.__Config_SMSC():
                            self.flag = 1
                            return self.__check_network_state()        
        return False
    
    async def __soft_restart(self):
        # self.command('AT+CFUN=1,1')
        if self.serial.open():
            # self.serial.write(b'AT+CFUN=1,1\r\n')
            self.command('AT+CFUN=1,1')
            print("[INFO] 已发送软重启命令，模块开始复位...")
            await asyncio.sleep(15)
        
        if self.serial.open():
            for i in range(8):

                if self.command("AT"):
                    return True           
                else:
                    await asyncio.sleep(1)
                    continue
        


    def __Config_SMSC(self):
        command = 'AT+CSCA='
        command += f'"{self.SMSC}",145' 
        if self.command(command):
            return True
        else:
            return False
    
    def __Config_ECM_OnlyMessageSent(self):
        # 设置为仅发送短信的ECM模式
        if self.command('AT+QCFG="usbnet",1'):
            return True
        else:
            return False
        
    def __Config_PDU_Mode(self):
        # 配置短消息模式为PDU模式
        if self.serial.open():
            if self.command("AT+CMGF=0"):
                return True
            else:
                return False
        
    def __check_network_state(self):

        LTE = '0,1'
        SMSC_number = '145'
        # 短消息服务中心地址是否正确设置
        # 查询网络注册状态,建议关注前两位， 0,1 是正常的，因为我们只发短信，不上网，故只要注册好即可
        """
        <n> 整型。控制非请求结果码的显示。
            0 禁止上报网络注册 URC
            1 允许上报网络注册 URC +CEREG: <stat>
            2 允许上报网络注册和位置信息 URC +CEREG: <stat>[,<tac>,<ci>[,<AcT>]]
        <stat> 整型。EPS 注册状态。
            0 未注册。MT 当前未搜索到待注册的运营商。
            1 已注册，归属地网络。
            2 未注册，但 MT 当前正尝试附着或搜索网络以进行注册。
            3 注册被拒绝。
            4 未知。
            5 已注册，漫游状态
        """
        if self.serial.open():
            self.serial.write(b"AT+CSCA?\r\n")
            response = self.__ResponseMessage()
            self.serial.write(b"AT+CEREG?\r\n")
            response += self.__ResponseMessage()
            if self.SMSC and SMSC_number and LTE in response:
                return True
            else:
                print(f"网络状态异常\n{response}")
                return False


    def __format_message(self):
        if (len(self.warn_str) == 8):
            str = self.process_warning()
        elif (len(self.warn_str) == 9):
            str = self.process_attendance()
            # print(f"str:\n{str}")
        elif (len(self.warn_str) == 2):
            str = self.process_IP()
            # print(f"str:\n{str}")
        else:
            print(f"传入参数错误！")
        return str;

    def process_warning(self):
        """({班级},{警告},{年},{月},{日},{时},{分},{秒})"""
        str = "班级位置:"
        str += f"{self.warn_str[0]}\n"
        str += "警告:"
        str += f"{self.warn_str[1]}\n"
        str += "时间:"
        str += f"{self.warn_str[2]}年{self.warn_str[3]}月{self.warn_str[4]}日 "
        str += f"{self.warn_str[5]}:{self.warn_str[6]}:{self.warn_str[7]}"
        return str
    
    def process_attendance(self):
        """({班级},{未到},{总人数},{年},{月},{日},{时},{分},{秒})"""
        str = "班级位置:"
        str += f"{self.warn_str[0]}\n"
        str += "迟到:"
        str += f"{self.warn_str[1]} / {self.warn_str[2]}\n"
        # str += "应到:"
        # str += f"{self.warn_str[2]}\n"
        str += "时间:"
        str += f"{self.warn_str[3]}年{self.warn_str[4]}月{self.warn_str[5]}日 "
        str += f"{self.warn_str[6]}:{self.warn_str[7]}:{self.warn_str[8]}"
        return str

    def process_IP(self):
        str = "班级位置:"
        str += f"{self.warn_str[0]}\n"
        # print(f"process_IP = {str}")
        str += "云上控制平台:"
        str += f"{self.warn_str[1]}\n"
        print(f"process_IP = {str}")
        return str
        
    def __to_UCS2(self):
        try:
            message = self.__format_message()
            print(f"message = {message}")
            if message is None:
                print(f"message为什么是空的")
            ucs2_bytes = message.encode('utf-16-be')
            result = bytearray()
            for char in message:
                code_point = ord(char)
                # 基本平面字符（U+0000~U+FFFF）
                if code_point <= 0xFFFF:
                    result.append(code_point >> 8)    # 高8位
                    result.append(code_point & 0xFF)  # 低8位
                # 辅助平面字符（如emoji）需代理对处理
                else:
                    code_point -= 0x10000
                    high_surrogate = 0xD800 | (code_point >> 10)
                    low_surrogate = 0xDC00 | (code_point & 0x3FF)
                    result.append(high_surrogate >> 8)
                    result.append(high_surrogate & 0xFF)
                    result.append(low_surrogate >> 8)
                    result.append(low_surrogate & 0xFF)
            ucs2_bytes = bytes(result)
            ucs2_message_hex =  ubinascii.hexlify(ucs2_bytes).decode('ascii').upper()
            return ucs2_message_hex
        except UnicodeError:
            print("警告信息转换UCS2失败")
        return  
    
    def _to_PDU(self):
        # 处理短信中心号码（假设已通过AT+CSCA设置，此处省略）
        # 表示短信中心号码已设置，无需在PDU中包含
        # smsc = "00"  这个似乎并不管用
        smsc_digits = self.SMSC.replace("+", "")
        smsc_len = len(smsc_digits) // 2

        if len(smsc_digits) % 2 != 0:
            smsc_digits += "F"  # 奇数长度时补F
        reversed_smsc = self.__reverse_phone_number(smsc_digits)
        # smsc_len_hex = f"{smsc_len:02X}"
        # smsc_part = f"{smsc_len_hex}91{reversed_smsc}"

        # 完整SMSC字段: 08 + SMSC部分
        # 08 = 表示后续SMSC地址长度(字节数), 一般是8个
        smsc = f"0891{reversed_smsc}"

        # 处理手机号码
        # 去掉+号，反转每两位数字
        """
        组装手机号码部分，根据 GSM 07.05 协议, <长度><类型><号码BCD码>
        长度:表示号码数字的个数(不包含+号,也不要包含添加的F符号),十六进制表示
        类型:91表示国际格式号码(带国家码)
        号码 BCD 码: 每两位数字反转后的编码
        """
        phone_digits = self.phone_number.replace("+", "")
        
        if len(phone_digits) % 2 != 0:
            phone_digits += "F"  # 奇数长度时补F
        reversed_phone = self.__reverse_phone_number(phone_digits)
        original_phone_len = len(phone_digits.replace("F", ""))
        # 格式化规则: 02X, 不包含F
        # 0: 填充字符0(不足位数时用 0 补),2: 最小宽度2位, X: 转换为大写十六进制
        phone_len_hex = f"{original_phone_len:02X}"
        phone_part = f"{phone_len_hex}91{reversed_phone}"

        
        encoded_message = self.__to_UCS2()
        # 因为每个UCS2字符要站2个字节
        message_bytes_len = len(encoded_message) // 2
        # print(f"message_bytes_len:{message_bytes_len}")
        message_bytes_len_hex = f"{message_bytes_len:02X}"

        MAX_SINGLE_MSG = 140  # 单条短信最大字节数
        UDH_LENGTH = 6        # 用户数据头(UDH)占用字节数
        MAX_CONTENT_PER_MSG = MAX_SINGLE_MSG - UDH_LENGTH  # 每片可用内容字1节数
        if message_bytes_len <= MAX_SINGLE_MSG:
            """
            组装完整PDU:
            [SMSC信息][基本参数][目标号码][编码类型][用户数据头标识][消息长度][消息内容]
            [基本参数]: 1100 固定值,11: 表示短信是 “提交” 类型(MT-MO,移动台到移动台)
                        00:表示无特殊设置(如状态报告请求)
            [编码类型]: 0008, UCS2 编码(适用于中文等所有 Unicode 字符)
            [用户数据头标识]: B0, 主要用于表明短信的用户数据部分包含用户数据头，这样接收方在解析短信时就会根据这个标识来处理额外的信息

            """
            # 0: 填充字符0(不足位数时用 0 补),2: 最小宽度2位, X: 转换为大写十六进制
            tp = f"1100{phone_part}0008B0{message_bytes_len_hex}{encoded_message}"
            tp_length = len(tp) // 2
            pdu = f"{smsc}{tp}"
            result = []
            result.append((pdu, tp_length))
            # print(f"encoded_message= {encoded_message}")
            # print(f"message_bytes_len_hex= {message_bytes_len_hex}")
            # pdu_length = len(pdu) // 2  
            # print(f"pdu = {pdu}")
            # print(f"pdu_length = {pdu_length}")
            # print("我发送了短消息")  
            return result
        
        num_segments = (message_bytes_len + MAX_CONTENT_PER_MSG - 1) // MAX_CONTENT_PER_MSG
        ref_id = urandom.getrandbits(8)  # 生成随机参考号(0-255)
        segments = []  # 存储分片PDU

        # 使用长短信专用编码(48=UCS2+用户数据头)
        # tp_dcs = "48"  0x48表示8-bit数据，不适合UCS2编码的长短信
        
        for seq in range(1, num_segments + 1):

            # segment_bytes = min(MAX_CONTENT_PER_MSG, message_bytes_len - (seq - 1) * MAX_CONTENT_PER_MSG)

            start_byte = (seq - 1) * MAX_CONTENT_PER_MSG
            end_byte = min(start_byte + MAX_CONTENT_PER_MSG, message_bytes_len)
            segment_bytes = end_byte - start_byte

            # 计算当前分片内容范围
            start_index = (seq - 1) * MAX_CONTENT_PER_MSG * 2  # 字符位置(*2因十六进制)
            end_index = start_index + MAX_CONTENT_PER_MSG * 2
            segment_content = encoded_message[start_index:end_index]
            
            # 2. 构建用户数据头(UDH)
            # 格式: [UDH长度][信息类型][信息长度][参考号][总分片][当前分片]
            ## 
            udh = (
                "06"+          # UDH长度=6字节(后续数据长度)
                "00"+          # IEI: 长短信标识(0x00)
                "03"+          # IEDL: 后续数据长度(3字节)
                f"{ref_id:02X}"+        # 参考号(1字节)
                f"{num_segments:02X}"+   # 总分片数(1字节)
                f"{seq:02X}"          # 当前分片序号(1字节)
            )
            
            # 3. 组合UDH和内容
            ud_content = udh + segment_content
            ud_length = len(ud_content) // 2  # 计算总字节数
            
            # 4. 构建完整TPDU
            # tp = f"1100{phone_part}{tp_dcs}{ud_length:02X}{ud_content}"
            # tp = f"1100{phone_part}0008{ud_bytes_len:02X}{ud_content}"
            ## "1100"
            tp = (
                "1100" +                            # 基本参数：TP-MTI=01 + TP-UDHI=1（启用UDH）
                f"{phone_part}" +                        # 目标地址
                "08" +                              # DCS: UCS2编码
                f"{ud_length:02X}" +          # UDL：用户数据长度
                f"{ud_content}"                           # UD（UDH头 + 短信内容）
            )
            tp_length = len(tp) // 2  # TPDU长度(字节)
            pdu = f"{smsc}{tp}"
            
            segments.append((pdu, tp_length))
        # print("我发送了长消息: {pdu}, {tp_len}")    
        return segments


    
    def __reverse_phone_number(self,phone_digits):
        """
        @param: 传过来的手机号字符串,需要将其2为翻转
        """
        # 反转后的号码字符串
        reversed_phone = ""
        for i in range(0, len(phone_digits), 2):
            pair = phone_digits[i:i+2]
            reversed_pair = pair[1] + pair[0]
            reversed_phone += reversed_pair
    
        return reversed_phone
                
    def __delete_sms_by_status(self):
        """按状态删除短信
        status: 0=未读, 1=已读, 2=已发送, 3=草稿, 4=所有
        """
        command = f'AT+CMGD=1, 4'
        if self.command(command):
            print(f"成功删除已发送的短信")
            return True
        else:
            print(f"删除短信失败")
            return False
        
    def __check_storage_status(self):
        """查询并打印所有可用存储位置的状态"""
        # 最大响应时间300ms
        time.sleep(0.5)
        if self.serial.open():
            self.serial.write(b"AT+CPMS?\r\n")
            time.sleep(0.3)
            response = self.serial.read(100) 
            # print(f"存储状态: {response}")

            cpms_start = response.find('+CPMS:')
            if cpms_start == -1:
                print("未找到+CPMS:响应")
                return None

            cpms_line = response[cpms_start:].split('\n', 1)[0]
             # 查找第一个"ME",的位置
            me_start = cpms_line.find('"ME",')
            if me_start == -1:
                print("未找到ME存储信息")
                return None
        
            # 提取并解析使用量和总容量
            data_part = cpms_line[me_start + 5:]  # 跳过"ME",
            parts = data_part.split(',', 2)
    
            if len(parts) < 2:
                print("无法解析ME存储数据")
                return None
        
            try:
                used = int(parts[0])
                total = int(parts[1])
                # print(f"ME存储容量: {used}/{total} 条短信")
        
                # 检查使用率并清理（如果超过60%）
                if used / total > 0.6:
                    print(f"ME存储使用率超过60%，尝试清理")
                    self.__delete_sms_by_status()
            
                return used, total
            except ValueError:
                print(f"解析数值失败: {parts}")
                return None
        
    
    def __store_to_me(self, pdu, tp_length):
        """将PDU存储到ME"""
        # PDU模式下存储
        command = f"AT+CMGW={tp_length},2"  # stat=2表示未发送状态
        if self.serial.open():
            if self.command(command):
                self.serial.write(pdu.encode())
                self.serial.write(bytes([26]))  # Ctrl+Z

                time.sleep(0.5)
                response = self.serial.read(200) 
                if self.ack in response or "+CMGW:" in response:
                    self.storage_index = int(response.split(":")[1].strip())
                    print(f"成功存储到ME,索引: {self.storage_index}")
                    return True
                else:
                    print("存储到ME失败")
                    return False

    
    def __send_stored_messages(self):
        """从ME发送存储的短信"""
        
        if self.storage_index:
            print(f"开始发送ME中存储的短信,索引: {self.storage_index}")
            # 使用AT+CMSS发送
            command = f"AT+CMSS={self.storage_index}"
            if self.command(command):
                time.sleep(0.5)
                response = self.serial.read(200) 
                if "+CMSS:" in response:
                    print("存储的短信发送成功")
                    self.__delete_stored_message()  # 发送成功后删除
                else:
                    print("存储的短信发送失败")
            else:
                print("发送存储的短信命令执行失败")
        else:
            return ;
        
    
    def __delete_stored_message(self):
        """删除已发送的存储短信"""
        if self.storage_index:
            command = f"AT+CMGD={self.storage_index}"
            self.command(command)
            self.storage_index = None
            print("已删除存储的短信")
    
    def get_RealTime(self):
        """
        0 查询通过网络同步的最新时间
        1 查询通过网络同步的最新时间计算出的当前 GMT 时间
        2 查询通过网络同步的最新时间计算出的当前本地时间

        返回的是一个包含：字符串元组
        return (year, month, day, hour, minute, second)        
        """
        if self.serial.open():
            self.serial.write(b"AT+QLTS=2\r\n")
            time.sleep(0.3)
            response = self.serial.read(200)
            if self.ack in response and response is not None:
                return self.__parse_time_response(response)
            else: 
                print(f"获取云上时间失败")
                return None
        else:
            return self.get_RealTime()

    def __parse_tuple_time_response(self, response):
        """从 AT+QLTS 响应中提取日期和时间元组""" 
        # 查找 +QLTS: 行
        start_index = response.find('+QLTS: "')
        if start_index == -1:
            print("未找到 +QLTS: 标记")
            return None
        
        # 提取引号内的时间字符串
        start = start_index + len('+QLTS: "')
        end = response.find('"', start)
        if end == -1:
            print("未找到闭合引号")
            return None
            
        time_str = response[start:end]
        # print(f"提取的时间字符串: {time_str}")
        
        if not time_str:
            print("时间字符串为空")
            return None
        
        # 分割日期和时间部分（增强错误处理）
        parts = time_str.split(',')
        if len(parts) < 2:
            print(f"分割失败, parts数量: {len(parts)}, 内容: {parts}")
            return None
            
        date_part, time_part = parts[0], parts[1]
        
        # 提取年月日
        date_parts = date_part.split('/')
        if len(date_parts) != 3:
            print(f"日期格式错误, parts: {date_parts}")
            return None
            
        year, month, day = date_parts
        
        # 提取时分秒（处理时区偏移）
        time_parts = time_part.split('+')
        if len(time_parts) < 1:
            print(f"时间格式错误, parts: {time_parts}")
            return None
            
        time_main = time_parts[0]  # 主时间部分
        time_sub_parts = time_main.split(':')
        if len(time_sub_parts) != 3:
            print(f"时分秒格式错误,parts: {time_sub_parts}")
            return None
            
        hour, minute, second = time_sub_parts
        
        return (year, month, day, hour, minute, second)        

    def __parse_time_response(self, response):
        """从 AT+QLTS 响应中提取日期和时间，并格式化为 YYMMDDHHMMSS 字符串""" 
        # 查找 +QLTS: 行
        start_index = response.find('+QLTS: "')
        if start_index == -1:
            print("未找到 +QLTS: 标记")
            return None
        
        # 提取引号内的时间字符串
        start = start_index + len('+QLTS: "')
        end = response.find('"', start)
        if end == -1:
            print("未找到闭合引号")
            return None
            
        time_str = response[start:end]
        if not time_str:
            print("时间字符串为空")
            return None
        
        # 分割日期和时间部分
        parts = time_str.split(',')
        if len(parts) < 2:
            print(f"分割失败, parts数量: {len(parts)}, 内容: {parts}")
            return None
            
        date_part, time_part = parts[0], parts[1]
        
        # 提取年月日（假设输入已保证两位数字）
        date_parts = date_part.split('/')
        if len(date_parts) != 3:
            print(f"日期格式错误, parts: {date_parts}")
            return None
            
        year, month, day = date_parts
        
        # 提取时分秒（处理时区偏移）
        time_parts = time_part.split('+')
        if len(time_parts) < 1:
            print(f"时间格式错误, parts: {time_parts}")
            return None
            
        time_main = time_parts[0]  # 主时间部分
        time_sub_parts = time_main.split(':')
        if len(time_sub_parts) != 3:
            print(f"时分秒格式错误, parts: {time_sub_parts}")
            return None
            
        hour, minute, second = time_sub_parts
        
        formatted_time = f"{year[-2:]}{month}{day}{hour}{minute}{second}" 
        
        return (formatted_time, year[:2])

# warn = Warning(serial)
# warn.Send_Message()
